package org.robotlegs.base
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	
	import org.robotlegs.core.IInjector;
	import org.robotlegs.core.IMediator;
	import org.robotlegs.core.IMediatorMap;
	import org.robotlegs.core.IReflector;
	

	/**
	 * 映射主程序 (contextView) Mediator
	 *
	 * 映射 contextView 到一个 mediator 是一个常见的模式. 
	 * 这是个特殊情况, 因为自动中介对 contextView 不起作用, 
	 * 因为它已经被添加到舞台上, 而不会再发出 mediatorMap 自动中介所需要的事件了. 
	 * 这个映射通常在持有 contextView 引用的 Context 的 setup() 方法里完成
	 * 
	 *----------------------------------------------------------------------------------------
	 * 通过 Mediator 访问 Model 和 Service
	 * 
	 * 你的 mediator 可以监听 Service 和 Model 类派出的系统事件来提高松耦合性. 
	 * 通过监听事件, 你的 mediator 不需要关心事件来源, 而只需直接使用事件携带的强类型的负载. 
	 * 因此, 多个 mediator 可以监听相同的事件然后根据所收到的数据调整自己的状态.
	 * 
	 * 
	 */
public class MediatorMap extends ViewMapBase implements IMediatorMap
{
	public function MediatorMap(contextView:DisplayObjectContainer, injector:IInjector, reflector:IReflector)
	{
		super(contextView, injector);
		
		this.reflector = reflector;
		
		// mappings - if you can do it with fewer dictionaries you get a prize
		this.mediatorByView = new Dictionary(true);
		this.mappingConfigByView = new Dictionary(true);
		this.mappingConfigByViewClassName = new Dictionary(false);
		this.mediatorsMarkedForRemoval = new Dictionary(false);
	}
	
	
	
	
	
	protected static const enterFrameDispatcher:Sprite = new Sprite();
	
	protected var mediatorByView:Dictionary;

	protected var mappingConfigByView:Dictionary;
	
	protected var mappingConfigByViewClassName:Dictionary;
	
	protected var mediatorsMarkedForRemoval:Dictionary;
	
	protected var hasMediatorsMarkedForRemoval:Boolean;
	
	protected var reflector:IReflector;

	
	
	
	/**
	 * 任何可以访问到 mediatorMap 实例的类都可以映射 Mediator. 这包括 Mediator, Context, 和 Command 类.
	 */
	public function mapView(viewClassOrName:*, mediatorClass:Class, injectViewAs:* = null, autoCreate:Boolean = true, autoRemove:Boolean = true):void
	{
		var viewClassName:String = reflector.getFQCN(viewClassOrName);
		
		if (mappingConfigByViewClassName[viewClassName] != null)
			throw new ContextError(ContextError.E_MEDIATORMAP_OVR + ' - ' + mediatorClass);
		
		if (reflector.classExtendsOrImplements(mediatorClass, IMediator) == false)
			throw new ContextError(ContextError.E_MEDIATORMAP_NOIMPL + ' - ' + mediatorClass);
		
		var config:MappingConfig = new MappingConfig();
		config.mediatorClass = mediatorClass;
		config.autoCreate = autoCreate;
		config.autoRemove = autoRemove;
		if (injectViewAs)
		{
			if (injectViewAs is Array)
			{
				config.typedViewClasses = (injectViewAs as Array).concat();
			}
			else if (injectViewAs is Class)
			{
				config.typedViewClasses = [injectViewAs];
			}
		}
		else if (viewClassOrName is Class)
		{
			config.typedViewClasses = [viewClassOrName];
		}
		mappingConfigByViewClassName[viewClassName] = config;
		
		if (autoCreate || autoRemove)
		{
			viewListenerCount++;
			if (viewListenerCount == 1)
				addListeners();
		}
		
		// This was a bad idea - causes unexpected eager instantiation of object graph 
		if (autoCreate && contextView && (viewClassName == getQualifiedClassName(contextView) ))
			createMediatorUsing(contextView, viewClassName, config);
	}
	

	public function unmapView(viewClassOrName:*):void
	{
		var viewClassName:String = reflector.getFQCN(viewClassOrName);
		var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
		if (config && (config.autoCreate || config.autoRemove))
		{
			viewListenerCount--;
			if (viewListenerCount == 0)
				removeListeners();
		}
		delete mappingConfigByViewClassName[viewClassName];
	}
	

	/**
	 * 有时候可能不希望或者不可能使用 view component 的自动中介. 在这种情况下可以手动创建一个 Mediator 类的实例
	 */
	public function createMediator(viewComponent:Object):IMediator
	{
		return createMediatorUsing(viewComponent);
	}
	

	public function registerMediator(viewComponent:Object, mediator:IMediator):void
	{
		var mediatorClass:Class = reflector.getClass(mediator);
		injector.hasMapping(mediatorClass) && injector.unmap(mediatorClass);
		injector.mapValue(mediatorClass, mediator);
		mediatorByView[viewComponent] = mediator;
		mappingConfigByView[viewComponent] = mappingConfigByViewClassName[getQualifiedClassName(viewComponent)];
		mediator.setViewComponent(viewComponent);
		mediator.preRegister();
	}
	

	public function removeMediator(mediator:IMediator):IMediator
	{
		if (mediator)
		{
			var viewComponent:Object = mediator.getViewComponent();
			var mediatorClass:Class = reflector.getClass(mediator);
			delete mediatorByView[viewComponent];
			delete mappingConfigByView[viewComponent];
			mediator.preRemove();
			mediator.setViewComponent(null);
			injector.hasMapping(mediatorClass) && injector.unmap(mediatorClass);
		}
		return mediator;
	}
	

	public function removeMediatorByView(viewComponent:Object):IMediator
	{
		return removeMediator(retrieveMediator(viewComponent));
	}
	

	public function retrieveMediator(viewComponent:Object):IMediator
	{
		return mediatorByView[viewComponent];
	}
	

	public function hasMapping(viewClassOrName:*):Boolean
	{
		var viewClassName:String = reflector.getFQCN(viewClassOrName);
		return (mappingConfigByViewClassName[viewClassName] != null);
	}
	

	public function hasMediatorForView(viewComponent:Object):Boolean
	{
		return mediatorByView[viewComponent] != null;
	}
	

	public function hasMediator(mediator:IMediator):Boolean
	{
		for each (var med:IMediator in mediatorByView)
			if (med == mediator)
				return true;
		return false;
	}

	
	
	
	protected override function addListeners():void
	{
		if (contextView && enabled)
		{
			contextView.addEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture, 0, true);
			contextView.addEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture, 0, true);
		}
	}
	
	
	protected override function removeListeners():void
	{
		if (contextView)
		{
			contextView.removeEventListener(Event.ADDED_TO_STAGE, onViewAdded, useCapture);
			contextView.removeEventListener(Event.REMOVED_FROM_STAGE, onViewRemoved, useCapture);
		}
	}
	
	
	protected override function onViewAdded(e:Event):void
	{
		if (mediatorsMarkedForRemoval[e.target])
		{
			delete mediatorsMarkedForRemoval[e.target];
			return;
		}
		var viewClassName:String = getQualifiedClassName(e.target);
		var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
		if (config && config.autoCreate)
			createMediatorUsing(e.target, viewClassName, config);
	}
	

	protected function createMediatorUsing(viewComponent:Object, viewClassName:String = '', config:MappingConfig = null):IMediator
	{
		var mediator:IMediator = mediatorByView[viewComponent];
		if (mediator == null)
		{
			viewClassName ||= getQualifiedClassName(viewComponent);
			config ||= mappingConfigByViewClassName[viewClassName];
			if (config)
			{
				for each (var claxx:Class in config.typedViewClasses) 
				{
					injector.mapValue(claxx, viewComponent);
				}
				mediator = injector.instantiate(config.mediatorClass);
				for each (var clazz:Class in config.typedViewClasses) 
				{
					injector.unmap(clazz);
				}
				registerMediator(viewComponent, mediator);
			}
		}
		return mediator;			
	}		
	

	protected function onViewRemoved(e:Event):void
	{
		var config:MappingConfig = mappingConfigByView[e.target];
		if (config && config.autoRemove)
		{
			mediatorsMarkedForRemoval[e.target] = e.target;
			if (!hasMediatorsMarkedForRemoval)
			{
				hasMediatorsMarkedForRemoval = true;
				enterFrameDispatcher.addEventListener(Event.ENTER_FRAME, removeMediatorLater);
			}
		}
	}
	

	protected function removeMediatorLater(event:Event):void
	{
		enterFrameDispatcher.removeEventListener(Event.ENTER_FRAME, removeMediatorLater);
		for each (var view:DisplayObject in mediatorsMarkedForRemoval)
		{
			if (!view.stage)
				removeMediatorByView(view);
			delete mediatorsMarkedForRemoval[view];
		}
		hasMediatorsMarkedForRemoval = false;
	}
}
}

class MappingConfig
{
	public var mediatorClass:Class;
	public var typedViewClasses:Array;
	public var autoCreate:Boolean;
	public var autoRemove:Boolean;
}
